package com.exmple.myapplication;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.observers.DefaultObserver;
import io.reactivex.schedulers.Schedulers;
import jp.co.canon.android.print.ij.usbsdk.CanonPermissionRequestCallback;
import jp.co.canon.android.print.ij.usbsdk.CanonPreparationCallback;
import jp.co.canon.android.print.ij.usbsdk.CanonPrintCallback;
import jp.co.canon.android.print.ij.usbsdk.CanonPrintDevice;
import jp.co.canon.android.print.ij.usbsdk.CanonPrintJob;
import jp.co.canon.android.print.ij.usbsdk.CanonPrintParams;
import jp.co.canon.android.print.ij.usbsdk.CanonPrinterStatus;
import jp.co.canon.android.print.ij.usbsdk.CanonUsbManager;

public class SecondFragment extends Fragment {

    private ActivityResultLauncher<String> stringActivityResultLauncher;
    private ActivityResultLauncher<Uri> resultLauncher;
    private ImageView img;
    private SharedPreferences sharedPreferences;
    private final String KEY_STR = "selectPath";
    private final String KEY_URI_STR = "selectUriPath";
    private TextView textPath;
    private final String TAG = getClass().getSimpleName();
    private Bitmap showingBitmap;
    private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
    private CanonPrintJob mJob;
    private CanonPrintDevice mSelectedPrinter;
    private Uri mSelectedFile;
    private Boolean isPrinting;
    private final Handler mHandler = new Handler(Looper.myLooper());

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getPrinterList();
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity().getApplicationContext());
        FirstFragment.selectPath = sharedPreferences.getString(KEY_STR, Environment.getExternalStorageDirectory().getAbsolutePath());
        stringActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.GetMultipleContents(), result -> {
            if (result == null) {
                return;
            }
            ArrayList<String> pathList = new ArrayList<>();
            for (Uri uri : result
            ) {
                String selectPath = ReaderUtils.getImageAbsolutePath(this.getActivity().getApplicationContext(), uri);
                pathList.add(selectPath);
            }
            Observable.create((ObservableOnSubscribe<Bitmap>) emitter -> {
                        Bitmap[] bitmap = new Bitmap[pathList.size()];
                        for (int i = 0; i < bitmap.length; i++) {
                            bitmap[i] = BitmapFactory.decodeFile(pathList.get(i));
                        }
                        showingBitmap = mergeBitmap(bitmap);
                        emitter.onNext(showingBitmap);

                    })
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<Bitmap>() {
                        @Override
                        public void onSubscribe(Disposable d) {

                        }

                        @Override
                        public void onNext(Bitmap s) {
                            img.setImageBitmap(s);
                        }

                        @Override
                        public void onError(Throwable e) {

                        }

                        @Override
                        public void onComplete() {

                        }
                    });

        });
        resultLauncher = registerForActivityResult(new ActivityResultContracts.OpenDocumentTree(), result -> {
            if (result == null) {
                return;
            }
            Uri uri = DocumentsContract.buildDocumentUriUsingTree(
                    result,
                    DocumentsContract.getTreeDocumentId(result)
            );
            try {
                FirstFragment.selectPath = ReaderUtils.getImageAbsolutePath(this.getActivity().getApplicationContext(), uri);
            } catch (ArrayIndexOutOfBoundsException exception) {
                FirstFragment. selectPath = Environment.getExternalStorageDirectory().getAbsolutePath();
            }
            textPath.setText(FirstFragment.selectPath);

            SharedPreferences.Editor edit = sharedPreferences.edit();
            edit.putString(KEY_STR, FirstFragment.selectPath);
            edit.apply();
            Log.i(TAG, "onActivityResult: " + "\r\n" + FirstFragment.selectPath);
        });

    }
    private void executePrint() {
        final Context applicationContext = this.getActivity().getApplicationContext();

        if (mSelectedPrinter == null) {
            Log.i(TAG,getString(R.string.printer_is_not_selected));
            return;
        }

        if (mSelectedFile == null || mSelectedFile == Uri.EMPTY) {
            Log.i(TAG,getString(R.string.file_is_not_selected));
            return;
        }

        // Check the width and height of custom paper size
        if (mJob.getPrintConfiguration(CanonPrintJob.Configuration.PaperSize) == CanonPrintParams.PAPERSIZE_USERDEFINITION ) {
            return;
        }

        // ManualDuplex setting
        mJob.setPrintConfiguration(CanonPrintJob.Configuration.ManualDuplex, CanonPrintParams.MANUAL_DUPLEX_SETTING_OFF);

        // Checks print configuration
        CanonPrintDevice.PrintableStatus checkStatus = mSelectedPrinter.isPrintableConfiguration(mJob);
        if (checkStatus != CanonPrintDevice.PrintableStatus.OK) {
            String checkMsg;
            switch (checkStatus) {
                case CONFLICT_BORDERLESS:
                    checkMsg = "无边距设置与所设介质和尺寸不匹配";
                    break;
                case CONFLICT_COLOR:
                    checkMsg = "色彩设置与所设介质不匹配";
                    break;
                case CONFLICT_DUPLEX:
                    checkMsg = "双面设置与所设介质和尺寸不匹配";
                    break;
                case CONFLICT_QUALITY:
                    checkMsg = "质量设置与所设介质不匹配";
                    break;
                case INAPPLICABLE_MEDIA:
                    checkMsg = "不适用的介质";
                    break;
                case INAPPLICABLE_SIZE:
                    checkMsg = "不适用的纸张尺寸";
                    break;
                case INAPPLICABLE_COPIES:
                    checkMsg = "不适用的打印份数";
                    break;
                case NULL_CAPABILITY:
                    checkMsg = "打印机能力获取失败";
                    break;
                case ERROR_CONFIGURATION:
                    checkMsg = "打印机配置错误";
                    break;
                case ERROR_USER_DEFINITION:
                    checkMsg = "自定义用纸尺寸设置错误";
                    break;
                default:
                    checkMsg = "其他无效的打印设置";
                    break;
            }
            Log.e(TAG,checkMsg);
            return;
        }

        // Disables/enables buttons
        isPrinting = true;
        if (CanonUsbManager.hasPermission(applicationContext, mSelectedPrinter)) {
            print(mSelectedPrinter);
        } else {
            boolean result = CanonUsbManager.requestPermission(
                    applicationContext,
                    mSelectedPrinter,
                    new CanonPermissionRequestCallback() {
                        @Override
                        public void onReceivePermissionGranted(CanonPrintDevice device, boolean isGranted) {
                            if (mSelectedPrinter.equals(device)) {
                                if (isGranted) {
                                    print(device);
                                } else {
                                    isPrinting = false;
                                    Log.e(TAG,getString(R.string.printer_is_not_granted));
                                }
                            }
                        }
                    }
            );
            if (!result) {
                isPrinting = false;
                Log.e(TAG,getString(R.string.failed_to_request_permission));
            }
        }
    }

    private void print(@NonNull final CanonPrintDevice printer) {
        // Sets a print file
        mJob.setPrintFile(mSelectedFile, CanonPrintParams.PAPERORIENTATION_AUTOMATIC, CanonPrintParams.SCALING_NONE, this.getActivity().getApplicationContext());


        // Executes printing
        if (!printer.print(mJob, new CanonPrintCallback() {
            @Override
            public void onChangedJobStatus(final CanonPrintJob job) {
                final CanonPrintJob.PrintJobStatus changedJobStatus = job.getStatus();
                mHandler.post(() -> {
                    String printedCountInfo = getString(R.string.printed_count_info,
                            job.getTotalPrintedCopy(),
                            job.getPrintConfiguration(CanonPrintJob.Configuration.Copies),
                            job.getTotalPrintedPage(),
                            job.getTotalPage()
                    );
                    Log.i(TAG, "onChangedJobStatus: "+getString(R.string.job_status,
                            updateJobStatusTexts(changedJobStatus.ordinal()),
                            String.format(Locale.ENGLISH, "%3.2f", (float) (job.getJobProgress() * 100.0f)),
                            printedCountInfo
                    ));

                    // Initializes the text to "Ready" when job has been finished
                    if (job.isFinished()) {
                        // Re-creates a job
                        mJob = createJob();
                    }
                });
            }

            @Override
            public void onChangedPrinterStatus(CanonPrintJob job, final CanonPrinterStatus status) {
                mHandler.post(() -> {
                    // Just shows the current status
                    String text = updateDeviceStatusTexts(status.getStatus().ordinal());
                    if (!status.getSupportCode().isEmpty()) {
                        text += getString(R.string.support_code, status.getSupportCode());
                    }
                    Log.i(TAG, "onChangedPrinterStatus: "+text);
                });
            }
        })) {
            mJob = createJob();
            Log.e(TAG,"打印失败");
        }
    }
    private String updateJobStatusTexts(int index) {
        String[] jobStatusNames = this.getContext().getResources().getStringArray(R.array.job_status);
        if (index > jobStatusNames.length) {
            return "";
        }
        return jobStatusNames[index];
    }
    private String updateDeviceStatusTexts(int index) {
        String[] deviceStatusNames = this.getActivity().getApplicationContext().getResources().getStringArray(R.array.device_status);
        if (index > deviceStatusNames.length) {
            return "";
        }
        return deviceStatusNames[index];
    }
    private void getPrinterList() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                final Context applicationContext = getActivity().getApplicationContext();

                try {
                    List<CanonPrintDevice> printerList = CanonUsbManager.getPrinterList(applicationContext);
                    selectPrinter(printerList);
                } catch (SecurityException e) {
                    CanonUsbManager.prepareToGetPrinterList(applicationContext, new CanonPreparationCallback() {
                        @Override
                        public void onSuccess() {
                            List<CanonPrintDevice> printerList = CanonUsbManager.getPrinterList(applicationContext);
                            selectPrinter(printerList);
                        }

                        @Override
                        public void onFailure() {
                            Log.i("onFailure: ", "获取打印机列表失败。");
                        }
                    });
                }
            }
        }).start();

    }
    private void selectPrinter(List<CanonPrintDevice> canonPrinterList) {
        if (canonPrinterList != null&&canonPrinterList.size()>0) {
            final CanonPrintDevice selected = canonPrinterList.get(0);

            if (CanonUsbManager.hasPermission(this.getActivity().getApplicationContext(), selected)) {
                mSelectedPrinter = selected;
                mJob = createJob();
            } else {
                boolean result = CanonUsbManager.requestPermission(
                        this.getActivity().getApplicationContext(),
                        selected,
                        new CanonPermissionRequestCallback() {
                            @Override
                            public void onReceivePermissionGranted(CanonPrintDevice device, boolean isGranted) {
                                if (selected.equals(device)) {
                                    mSelectedPrinter = device;
                                    mJob = createJob();

                                }
                            }
                        }
                );
                if (!result) {
                    Log.i("onFailure:", getString(R.string.failed_to_request_permission));
                }
            }

        }
    }
    private CanonPrintJob createJob() {
        CanonPrintJob job = new CanonPrintJob();
        if (mSelectedPrinter != null) {
            job.setDefaultPrintConfiguration(this.getActivity().getApplicationContext(), mSelectedPrinter);
            job.setPrintConfiguration(CanonPrintJob.Configuration.MediaType, 1);
            job.setPrintConfiguration(CanonPrintJob.Configuration.PaperSize, 3);
            job.setPrintConfiguration(CanonPrintJob.Configuration.Borderless, 1);
        }
        return job;
    }
    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {

        @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.fragment_second, null);
        img = view.findViewById(R.id.image_second);
        Button btnSelect = view.findViewById(R.id.button_second_4);
        textPath = view.findViewById(R.id.tv_path);
        textPath.setText(FirstFragment.selectPath);
        ScrollView scrollView  =view.findViewById(R.id.sv);
        Button btnCut = view.findViewById(R.id.button_second_3);
        Button btnCutAll = view.findViewById(R.id.button_second_5);
        btnCutAll.setOnClickListener(v -> {
            Bitmap bitmapByView = screenShotWholeScreen(getActivity());
            saveBitmap(bitmapByView,"sc",true);
        });
        btnCut.setOnClickListener(v -> {
            Bitmap bitmapByView = getBitmapByView(scrollView);
            saveBitmap(bitmapByView,"sl");
        });
        Button btnSave = view.findViewById(R.id.button_second_2);
        btnSave.setOnClickListener(v -> saveBitmap(showingBitmap, ""));
        Button btnSelectDir = view.findViewById(R.id.button_second);
        btnSelectDir.setOnClickListener(v -> selectDir());
        btnSelect.setOnClickListener(v -> selectFile());
        view.findViewById(R.id.button_second_7).setOnClickListener(v -> saveBitmap(showingBitmap,"",true));
        return view;

    }

    private void saveBitmap(Bitmap showingBitmap, String s) {
        saveBitmap(showingBitmap, s,false);
    }

    public void selectDir() {
        resultLauncher.launch(Uri.parse(Environment.getExternalStorageDirectory().getAbsolutePath()));
    }

    public void selectFile() {
        stringActivityResultLauncher.launch("image/*");
    }

    public void saveBitmap(Bitmap bitmap, String fileName, boolean b) {
        if (bitmap == null) {
            return;
        }
        if (TextUtils.isEmpty(fileName) || fileName == null) {
            fileName = simpleDateFormat.format(new Date(System.currentTimeMillis()));
        }
        String finalFileName = fileName;
        Observable.create((ObservableOnSubscribe<Boolean>) emitter -> {

                    File file = new File(FirstFragment.selectPath + File.separator, finalFileName + ".jpg");
                    FileOutputStream fos = new FileOutputStream(file);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
                    fos.close();
                    if (b){
                        mSelectedFile = Uri.fromFile(file);
                        executePrint();
                    }
                    emitter.onNext(true);
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DefaultObserver<Boolean>() {
                    @Override
                    public void onNext(Boolean aBoolean) {
                        Toast.makeText(SecondFragment.this.getActivity().getApplicationContext(), "保存完成", Toast.LENGTH_SHORT).show();

                    }

                    @Override
                    public void onError(Throwable e) {
                        Toast.makeText(SecondFragment.this.getActivity().getApplicationContext(), "保存失败:\r\n" + e.toString(), Toast.LENGTH_SHORT).show();

                    }

                    @Override
                    public void onComplete() {
                        Toast.makeText(SecondFragment.this.getActivity().getApplicationContext(), "保存完成", Toast.LENGTH_SHORT).show();
                    }
                });
    }


    //多图合成一个长图

    public Bitmap mergeBitmap(@NonNull Bitmap... bitmaps) {
        int totalH = 0;
        int maxW = 0;
        for (Bitmap b : bitmaps) {
            maxW = Math.max(maxW, b.getWidth());
        }
        ArrayList<Bitmap> bitmapArrayList = new ArrayList<>();
        for (Bitmap b : bitmaps) {
            Matrix matrix = new Matrix();
            float i = (maxW * 1.0f) / (b.getWidth() * 1.0f);
            matrix.setScale(i, i);
            Bitmap bitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, false);
            bitmapArrayList.add(bitmap);
            totalH += bitmap.getHeight();
        }
        Bitmap bitmap = Bitmap.createBitmap(maxW, totalH, bitmaps[0].getConfig());
        Canvas canvas = new Canvas(bitmap);
        int top = 0;
        for (Bitmap b : bitmapArrayList) {
            canvas.drawBitmap(b, 0, top, null);
            top += b.getHeight();
        }
        EditText editText = new EditText(getContext());
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
        return bitmap;
    }

    /**
     * 截取除了导航栏之外的整个屏幕
     */
    public Bitmap screenShotWholeScreen(Activity activity) {
        View dView = activity.getWindow().getDecorView();
        dView.setDrawingCacheEnabled(true);
        dView.buildDrawingCache();
        return Bitmap.createBitmap(dView.getDrawingCache());
    }

    /**
     * 截取scrollview的屏幕
     *
     * @param scrollView
     * @return
     */
    public Bitmap getBitmapByView(ScrollView scrollView) {
        int h = 0;
        Bitmap bitmap;
        // 获取listView实际高度
        for (int i = 0; i < scrollView.getChildCount(); i++) {
            h += scrollView.getChildAt(i).getHeight();
            scrollView.getChildAt(i).setBackgroundResource(R.drawable.white_drawable);
        }

        // 创建对应大小的bitmap
        bitmap = Bitmap.createBitmap(scrollView.getWidth(), h,
                Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(bitmap);
        scrollView.draw(canvas);
        return bitmap;
    }
    public Bitmap getBitmapByViews(Context context,View... views) {
        ScrollView scrollView = new ScrollView(context);
        ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        scrollView.setLayoutParams(layoutParams);
        for (View v:views
             ) {
            ViewGroup.LayoutParams params = v.getLayoutParams();
            params.width =ViewGroup.LayoutParams.MATCH_PARENT;
            params.height =ViewGroup.LayoutParams.WRAP_CONTENT;
            v.setLayoutParams(layoutParams);
            scrollView.addView(v);
        }

        return getBitmapByView(scrollView);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
    }


}